package com.gitee.wsl.data.loader


/**
 * A class represents an asynchronous resource loading.
 */
 sealed interface ResourceResult<out T> {

    /**
     * Source from where data has been loaded.
     */
     val source: DataSourceType

    /**
     * Represents the resource is still in the loading state.
     */
     data class Loading(
        val progress: Float,
        override val source: DataSourceType = DataSourceType.None,
    ) : ResourceResult<Nothing>

    /**
     * Represents the resource as a successful outcome.
     */
     data class Success<out T>(
        val value: T,
        override val source: DataSourceType = DataSourceType.None,
    ) : ResourceResult<T>

    /**
     * Represents the resource as a failure outcome.
     */
     data class Failure(
        val exception: Throwable,
        override val source: DataSourceType = DataSourceType.None,
    ) : ResourceResult<Nothing>
}

/**
 * Returns true if the resource still in the loading state, false otherwise.
 */
 val ResourceResult<*>.isLoading: Boolean
    get() = this is ResourceResult.Loading

/**
 * Returns true if the resource represents a successful outcome, false otherwise.
 */
 val ResourceResult<*>.isSuccess: Boolean
    get() = this is ResourceResult.Success

/**
 * Returns true if the resource represents a failure outcome, false otherwise.
 */
 val ResourceResult<*>.isFailure: Boolean
    get() = this is ResourceResult.Failure

/**
 * Returns [Success] with the [transform] function applied on the value if the resource represents success.
 * or [Failure] with the original exception if the resource represents failure.
 */
 inline fun <T, R> ResourceResult<T>.map(transform: (T) -> R): ResourceResult<R> = when (this) {
    is ResourceResult.Loading -> ResourceResult.Loading(progress, source)
    is ResourceResult.Success -> ResourceResult.Success(transform(value), source)
    is ResourceResult.Failure -> ResourceResult.Failure(exception, source)
}

/**
 * Returns value if the resource is [Success] or `null` otherwise.
 */
 fun <T> ResourceResult<T>.getOrNull(): T? = when (this) {
    is ResourceResult.Loading -> null
    is ResourceResult.Success -> value
    is ResourceResult.Failure -> null
}

/**
 * Returns value if the resource is [Success], result of [onFailure] function if it is [Failure]
 * or result of [onLoading] function if it is [Loading].
 */
 inline fun <T> ResourceResult<T>.getOrElse(
    onLoading: (Float) -> T,
    onFailure: (Throwable) -> T,
): T = when (this) {
    is ResourceResult.Loading -> onLoading(progress)
    is ResourceResult.Success -> value
    is ResourceResult.Failure -> onFailure(exception)
}